package org.tigr.htc.common;

/*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

public class FIFOSemaphore extends Semaphore {
	
	protected final WaitQueue queue = new WaitQueue();
	
	public FIFOSemaphore(int p_count) {
		super(p_count);
	}
	
	public void acquire() throws InterruptedException {
		if (Thread.interrupted()) { 
			throw new InterruptedException();
		}
		
		WaitNode node = null;
		
		synchronized(this) {
			if (count > 0) {
				--count;
				return;
			} else {
				node = new WaitNode();
				queue.enq(node);
			}
		}
		
		node.doWait();
	}
	
	public synchronized void release() {
		for (;;) {
			WaitNode node = queue.deq();
			
			if (node == null) {
				++count;
				return;
			} else if (node.doNotify()) {
				return;
			}
		}
	}



	protected static class WaitNode {
		boolean released = false;
		WaitNode next = null;
	
		synchronized void doWait() throws InterruptedException {
			try {
				while (! released) {
					wait();
				}
			} catch (InterruptedException ie) {
				if (!released) {
					released = true;
					throw ie;
				} else {
					Thread.currentThread().interrupt();
				}
			}
		}
	
		synchronized boolean doNotify() {
			if (released) {
				return false;
			} else {
				released = true;
				notify();
				return true;
			}
		}
	}

	protected static class WaitQueue {
		protected WaitNode head = null;
		protected WaitNode last = null;
	
		protected void enq(WaitNode node) {
			if (last == null) {
				head = last = node;
			} else {
				last.next = node;
				last = node;
			}
		}
	
		protected WaitNode deq() {
			WaitNode node = head;
		
			if (node != null) {
				head = node.next;
				if (node != null) {
					last = null;
				}
				node.next = null;
			}
			return node;
		}
	}
}





